package gbench.commonApp.data;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.Jdbc;
import gbench.common.tree.LittleTree.Jdbc.DataManipulation;
import gbench.common.tree.LittleTree.Jdbc.IJdbcSession;
import gbench.common.tree.LittleTree.Jdbc.JdbcExecute;
import gbench.common.tree.LittleTree.Jdbc.JdbcQuery;

import static gbench.common.tree.LittleTree.IRecord.REC;

/**
 *  数据应用：
 *  用户可以通过继承DataApp 来使用他的数据接口 SimpleDatabase，
 *  不过用户需要安装配重相应的驱动程序
 *  
 * @author gbench
 *
 */
public abstract class DataApp {
    
    /**
     * SimpleDatabase 简单数据库
     * @author gbench
     *
     */
    public interface SimpleDatabase {
        
        /**
         * 获取代理结构对象 一个类似于下面的的结构
         * 可以作为访问:sqlinterceptor，sqlpattern_preprocessor，jdbc_postprocessorjdbc等对象的一个入口。    
         * params: ""
         * method:public abstract gbench.common.tree.LittleTree$IRecord 
         *  gbench.demo.data.DataApp$SimpleDatabase.getProxy() 
         * sqlinterceptor:(null) sqlpattern_preprocessor:(null)  
         * jdbc_postprocessor:(null)   
         * jdbc:gbench.common.tree.LittleTree$Jdbc@26f67b76    
         * 
         * @return sqlpattern_preprocessor,interceptor,posprocessor 等内部结构。
         */
        IRecord getProxy();
        
        /**
         * 创建一个数据库
         * @param dbName 数据库名称
         */
        @JdbcQuery("create database''{0}'' default character set utf8mb4")
        void createDatabase(final String dbName);
        
        /**
         * 可以返回一些简单的类型：非空则认为true
         * @param tableName
         * @return 表格是否存在。
         */
        @JdbcQuery("show tables like ''{0}''")
        boolean exists(final String tableName);
        
        /**
         * 删除一张表
         * @param tableName
         */
        @JdbcExecute("drop table if exists {0}")
        void dropTable(final String tableName);
        
        /**
         * 查询用户表记录
         * @param tableName
         * @return
         */
        @JdbcQuery("select * from {0}")
        List<IRecord> getAll(final String tableName);
        
        /**
         * 查询用户表记录
         * @param tableName
         * @return
         */
        @JdbcQuery("select * from {0} limit {1}")
        List<IRecord> getAll(final String tableName, final int maxSize);
    }
    
    /**
     * 关键数据结构结构的重新加载 <br>
     * 会重新生成成员：<br>
     * database<br>
     * proxy<br>
     * jdbc<br>
     * 
     * @param cfg 数据应用的配置文件:<br>
     * 例如：url,driver,user,password的数据驱动相关参数
     * @return DataApp
     */
    public DataApp reload(IRecord cfg) {
        database = Jdbc.newInstance(SimpleDatabase.class,cfg==null?this.cfg:cfg);
        proxy = database.getProxy();
        //System.out.println(proxy);
        jdbc = proxy.findOne(Jdbc.class);
        return (DataApp)this;
    }// reload
    
    /**
     * 关键数据结构结构的重新加载 <br>
     * 会重新生成成员：<br>
     * database<br>
     * proxy<br>
     * jdbc<br>
     * 
     * @return DataApp
     */
    public DataApp reload() {
        return this.reload(null);
    }// reload
    
    // 数据库应用的配置：这是默认配置，可以通过继承制给与更改。
    protected IRecord cfg = REC(
        "url", "jdbc:mysql://localhost:3306/information_schema?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=True",
        "driver", "com.mysql.cj.jdbc.Driver",
        "user", "root",
        "password", "123456"
    );// cfg
    
    /**
     * 返回默认数据库对象
     * @return 默认数据库对象
     */
    public SimpleDatabase getDataBase() {
        return this.database;
    }
    
    /**
     * 返回数据库
     * @return 数据库对象
     */
    public <T extends SimpleDatabase> T getDataBase( final Class<T> dbclazz ) {
        return this.getDataBase(dbclazz, this.cfg);
    }
    
    /**
     * 返回数据库
     * 
     * @param <T> SimpleDatabase 的数据库接口类型
     * @param dbclazz 数据库接口类
     * @param cfg 配置文件, 需要包含 url,driver,user,password keys
     * @return @return 数据库对象
     */
    public <T extends SimpleDatabase> T getDataBase( final Class<T> dbclazz, final IRecord cfg ) {
        return Jdbc.newInstance(dbclazz,cfg);
    }
    
    /**
     * 指定session 执行DataManipulation．:Session 是Monad对象。因此可以进行函数式的状态编程<br>
     * 
     * 发起创建一个IJdbcSession对象，并通过IJdbcSession急性数据库操作<br>
     * 事务处理,每一个事务，系统会动态的创建出一个 session 对象（IJdbcSession），这个Session 对象拥有一个UUID类型的对象标识。<br>
     * 在一次事务性的会话IJdbcSession中：共享一个数据库连接，并且出现操作失败（sql操作)，将给予先前的动作回滚．<br>
     * 事务只能对DML语句进行操作，对数据定义类语句DDL无法操作，例如建表、建立索引、建立分区等。<br>
     * 一般采用如下方式调用此函数：<br>
     * jdbc.withTransaction(sess->{session.sql2records("show databases");});<br>
     * 
     * @param dm ：DataManipulation 代表，数据操作的具体过程 dm 的数据如果需要会馆请使用dm所提供的session 来操作数据,通常采用lamba表达式来给予
     * 创建操作过程：sess->{写入你的操作代码}. 需要注意对于withTransaction创建的会话IJdbcSession 是以monad 容器。其初始数据为Object类型
     * 值为null.
     * @return ret返回值boolean值 ,exception： 异常类型,throwable:异常类型,用于动态代理的默认函数，参见Jdbc.newInstance
     */
    public synchronized IRecord withTransaction (final DataManipulation<IJdbcSession<UUID,Object>> dm) {
        return this.withTransaction(dm, (IJdbcSession<UUID,Object>)null,(Map<Object,Object>)null);
    }
    
    /**
     * 指定session 执行DataManipulation．:Session 是Monad对象。因此可以进行函数式的状态编程<br>
     * 
     * 发起创建一个IJdbcSession对象，并通过IJdbcSession急性数据库操作<br>
     * 事务处理,每一个事务，系统会动态的创建出一个 session 对象（IJdbcSession），这个Session 对象拥有一个UUID类型的对象标识。<br>
     * 在一次事务性的会话IJdbcSession中：共享一个数据库连接，并且出现操作失败（sql操作)，将给予先前的动作回滚．<br>
     * 事务只能对DML语句进行操作，对数据定义类语句DDL无法操作，例如建表、建立索引、建立分区等。<br>
     * 一般采用如下方式调用此函数(使用Jdbc.M设置 session属性)：<br>
     * jdbc.withTransaction(sess->{session.sql2records("show databases");},<br>
     * Jdbc.M(SqlPatternPreprocessor.class,spp));<br>
     * 
     * @param dm ：DataManipulation 代表，数据操作的具体过程 dm 的数据如果需要会馆请使用dm所提供的session 来操作数据,通常采用lamba表达式来给予
     * 创建操作过程：sess->{写入你的操作代码}. 需要注意对于withTransaction创建的会话IJdbcSession 是以monad 容器。其初始数据为Object类型
     * 值为null.
     * @param sessAttributes 附加到sess上的属性信息。注入到 sess 上下文中的属性数据{(key,value)}
     * @return ret返回值boolean值 ,exception： 异常类型,throwable:异常类型,用于动态代理的默认函数，参见Jdbc.newInstance
     */
    public synchronized IRecord withTransaction (final DataManipulation<IJdbcSession<UUID,Object>> dm,
        final Map<Object,Object>sessAttributes) {
        return this.withTransaction(dm,(IJdbcSession<UUID,Object>)null,sessAttributes);
    }
    
    /**
     * 指定session 执行DataManipulation．:Session 是Monad对象。因此可以进行函数式的状态编程<br>
     * 发起创建一个IJdbcSession对象，并通过IJdbcSession急性数据库操作<br>
     * 事务处理,每一个事务，系统会动态的创建出一个 session 对象，这个Session 对象拥有一个UUID类型的对象标识。<br>
     * 在一次事务性的会话IJdbcSession中：共享一个数据库连接，并且出现操作失败（sql操作)，将给予先前的动作回滚．<br>
     * 事务只能对DML语句进行操作，对数据定义类语句DDL无法操作，例如建表、建立索引、建立分区等。<br>
     * @param dm 数据操作的具体过程 dm 的数据如果需要会馆请使用dm所提供的session 来操作数据
     * @param sess 数据操作所在的会话会话对象,其实就是对一个Connection的包装.需要注意对于withTransaction创建的会话IJdbcSession 是以monad 容器。其初始数据为Object类型
     * 值为null.
     * @param sessAttributes 附加到sess上的属性信息。注入到 sess 上下文中的属性数据{(key,value)}
     * @return ret返回值boolean值 ,exception： 异常类型,throwable:异常类型,用于动态代理的默认函数，参见Jdbc.newInstance
     */
    public synchronized IRecord withTransaction (final DataManipulation<IJdbcSession<UUID,Object>>  dm,
            final IJdbcSession<UUID,Object> sess, final Map<Object,Object>sessAttributes) {
        return this.jdbc.withTransaction(dm, sess, sessAttributes);
    }
    
    /**
     * 返回 数据应用的配置
     * @return 数据应用的配置
     */
    public IRecord getConfigure() {
        return this.cfg;
    }
    
    protected SimpleDatabase database = Jdbc.newInstance(SimpleDatabase.class,cfg);// 默认的数据库对象
    protected IRecord proxy = database.getProxy();// 默认的代理结果
    protected Jdbc jdbc = proxy.findOne(Jdbc.class);// 默认的jdbc对象
    
    public static Class<String> cstr=String.class;
    public static Class<Integer> cint=Integer.class;
    public static Class<Double> cdbl=Double.class;
    public static Class<Short> cshort=Short.class;
    public static Class<Boolean> cbool=Boolean.class;
    public static Class<Character> cchar=Character.class;
    public static Class<Byte> cbyte=Byte.class;
    public static Class<Long> clng=Long.class;
    public static Class<Float> cflt=Float.class;
    public static Class<IRecord> crec=IRecord.class;
    public static Class<Object> cobj=Object.class;
}
